/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package session;

import event.DeliverEvent;
import event.JobSendableEvent;
import event.RecoveryEvent;
import event.TrustEvent;
import java.util.ArrayList;
import util.FileHandler;
import util.Timer;
import net.sf.appia.core.AppiaEventException;
import net.sf.appia.core.Direction;
import net.sf.appia.core.Event;
import net.sf.appia.core.Layer;
import net.sf.appia.core.Session;
import net.sf.appia.core.events.channel.ChannelInit;
import net.sf.appia.protocols.common.RegisterSocketEvent;
import util.Constants;
import util.Debug;
import util.Process;
import util.Timer.OnTimeoutListener;

/**
 *
 * @author Gustavo
 */
public class EventualLeaderDetectorSession extends Session {

    private int delay;
    private ArrayList<Process> candidates;
    private String leader;
    private Class current = EventualLeaderDetectorSession.class;
    private Debug debug = Debug.getInstance();
    private Timer timer;
    private static final Process sourceProcess = Constants.SOURCE_PROCESS;
    
    public EventualLeaderDetectorSession(Layer layer) {
        super(layer);
    }

    @Override
    public void handle(Event event) {
        if (event instanceof ChannelInit) {
            handleChannelInit((ChannelInit) event);
        } else if (event instanceof RegisterSocketEvent) {
            handleRegisterSocket((RegisterSocketEvent) event);
        } else if (event instanceof RecoveryEvent) {
            handleRecoveryEvent((RecoveryEvent) event);
        } else if (event instanceof DeliverEvent) {
            handleDeliverEvent((DeliverEvent) event);
        }
    }

    private void handleChannelInit(ChannelInit init) {
        try {
            sourceProcess.setEpoch(0);
            delay = 0;
            leader = null;
            candidates = new ArrayList<Process>();
            timer = Timer.getInstance();
            timer.setOnTimeoutListener(
                    new OnTimeoutListener() {

                        @Override
                        public void onTimeoutListener() {
                            handleTimeout();
                        }
                    });

            init.go();
        } catch (AppiaEventException e) {
            debug.print(Debug.ERROR, current, e.getLocalizedMessage());
        }
    }

    private void handleRegisterSocket(RegisterSocketEvent event) {
        try {
            event.go();
        } catch (AppiaEventException ex) {
            debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
        }
    }

    private void handleRecoveryEvent(RecoveryEvent recoveryEvent) {
        leader = Constants.maxRank();
        triggerTrustLeader(recoveryEvent);
        delay = Constants.getDelta();
        retrieve();
        sourceProcess.incrementEpoch();
        store(sourceProcess.getEpoch());

        triggerHeartBeatMessage(recoveryEvent);

        candidates = new ArrayList<Process>();
        timer.startTime(delay);

    }

    private void triggerTrustLeader(RecoveryEvent recoveryEvent) {
        TrustEvent trusEvent = new TrustEvent(leader);
        trusEvent.setChannel(recoveryEvent.getChannel());
        trusEvent.setDir(Direction.UP);
        trusEvent.setSourceSession(this);
        try {
            trusEvent.init();
            trusEvent.go();
        } catch (AppiaEventException e) {
            debug.print(Debug.ERROR, current, e.getLocalizedMessage());
        }
    }

    private void handleTimeout() {
        String newLeader = select();

        if (newLeader.compareTo(leader) != 0) {
            delay += Constants.getDelta();
            leader = newLeader;

            //TODO trigger trustLeader
            //triggerTrustLeader(null);
        }

        //TODO 
        //sendHeartBeatMessage(null);

        candidates = new ArrayList<Process>();
        timer.startTime(delay);
    }

    private void handleDeliverEvent(DeliverEvent deliverEvent) {
        
    }
        
    private String select() {
        return Constants.maxRank(candidates);
    }

    private void triggerHeartBeatMessage(Event e) {
        ArrayList<Process> process = Constants.getAllProcess();

        for (Process p : process) {
            JobSendableEvent event = new JobSendableEvent();
            event.setId( p.getEpoch() );
            event.dest = p.getAddress();
            event.setMessage(Constants.buildMessage(Constants.HEARTBEAT_MESSAGE, p));
            try {
                event.asyncGo(e.getChannel(), Direction.DOWN);
            } catch (AppiaEventException ex) {
                ex.printStackTrace();
            }
        }
    }

    private void store(int epoch) {
        FileHandler.save(Constants.EPOCH_FILENAME, String.valueOf(epoch), false);
    }

    private void retrieve() {
        String[] lines = FileHandler.load(Constants.EPOCH_FILENAME);
        String sEpoch = lines[0];
        
        sourceProcess.setEpoch(Integer.parseInt(sEpoch) );
    }
}
